package com.sky.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.resource.DefaultClientResources;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePool;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * redisTemplate配置类，可以不写
 */
@Slf4j
@Configuration
public class RedisConfiguration {

    @Value("${sky.redis.host}")
    private String redisHost;

    @Value("${sky.redis.port}")
    private int redisPort;

    @Value("${sky.redis.password:}")
    private String redisPassword;

    @Value("${sky.redis.database:0}")
    private int redisDatabase;

    @PostConstruct
    public void init() {
        log.info("Redis配置信息 - host: {}, port: {}, password: {}, database: {}",
                redisHost, redisPort,
                redisPassword.isEmpty() ? "无密码" : redisPassword,
                redisDatabase);
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        log.info("开始初始化 RedisTemplate...");

        // 1. 创建 RedisTemplate 并设置连接工厂
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        log.info("RedisTemplate 已绑定连接工厂: {}", connectionFactory.getClass().getSimpleName());

        // 2. 配置序列化器
        // 2.1 JSON 序列化器（用于 Value）
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        jacksonSerializer.setObjectMapper(objectMapper);
        log.info("Jackson2JsonRedisSerializer 已配置，支持日期格式: yyyy-MM-dd HH:mm:ss");

        // 2.2 字符串序列化器（用于 Key）
        StringRedisSerializer stringSerializer = new StringRedisSerializer();

        // 3. 设置序列化规则
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(jacksonSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(jacksonSerializer);
        log.info("RedisTemplate 序列化配置完成: Key={}, Value={}",
                stringSerializer.getClass().getSimpleName(),
                jacksonSerializer.getClass().getSimpleName());

        // 4. 其他配置
        redisTemplate.setEnableTransactionSupport(true);
        redisTemplate.afterPropertiesSet();
        log.info("RedisTemplate 初始化完成，事务支持已开启");

        return redisTemplate;
    }

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {

        // 1. Redis 服务器配置
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        redisConfig.setHostName(redisHost);  // 使用成员变量
        redisConfig.setPort(redisPort);      // 使用成员变量
        redisConfig.setDatabase(redisDatabase);  // 使用成员变量

        // 如果密码不为空则设置
        if (!redisPassword.isEmpty()) {
            redisConfig.setPassword(RedisPassword.of(redisPassword));
        }

        // 2. 连接池配置
        GenericObjectPoolConfig<?> poolConfig = new GenericObjectPoolConfig<>();
        poolConfig.setMaxTotal(32);         // 最大连接数
        poolConfig.setMaxIdle(16);          // 最大空闲连接
        poolConfig.setMinIdle(8);           // 最小空闲连接
        poolConfig.setMaxWait(Duration.ofSeconds(10));  // 获取连接的最大等待时间
        poolConfig.setTestOnBorrow(true);   // 从连接池获取连接时是否测试连接有效性

        // 3. 客户端配置（包含连接池配置）
        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
                .poolConfig(poolConfig)  // 设置连接池配置
                .commandTimeout(Duration.ofSeconds(5))
                .clientResources(DefaultClientResources.builder().build())
                .clientOptions(ClientOptions.builder()
                        .socketOptions(SocketOptions.builder()
                                .connectTimeout(Duration.ofSeconds(5))
                                .build())
                        .build())
                .build();

        // 4. 创建连接工厂
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfig, clientConfig);
        factory.afterPropertiesSet();
        return factory;
    }

    // 可选：配置Redis缓存管理器（用于Spring Cache注解）
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 默认缓存配置
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30))  // 默认缓存过期时间30分钟
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))  // key序列化
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))  // value序列化
                .disableCachingNullValues();  // 不缓存null值

        // 自定义缓存配置（可针对不同缓存名设置不同过期时间）
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("userCache", defaultCacheConfig.entryTtl(Duration.ofHours(1)));  // 用户缓存1小时
        cacheConfigurations.put("productCache", defaultCacheConfig.entryTtl(Duration.ofMinutes(10)));  // 商品缓存10分钟

        // 创建缓存管理器
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaultCacheConfig)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }
}
